<!--
    by: yuanbiao 2020年09月23日
    name: x-video-progress
    notes:
-->
<template>
  <div ref="progress"
    :id="`xVideoProgress_${id}`"
    :class="[`${prefixCls}`, vertical ? `${prefixCls}-vertical` : '']"
    @touchstart.stop="tapHandler"
    @mousedown.stop="tapHandler"
  >
    <div :class="`${prefixCls}__slider`"
      :style="sliderStyle"
      @touchstart.stop="touchstartHandler"
      @mousedown.stop="mousedownHandler"
    ></div>
  </div>
</template>

<script>
import { debounce } from '@/script/utils.js'
const prefixCls = 'x-video-progress'
export default {
  name: 'x-video-progress',
  props: {
    vertical: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      prefixCls: prefixCls,
      id: `${Math.random()}`.substring(2),
      sliderData: {
        v0: 0,
        v1: 0,
        _v0: 0,
        rate: 0,
        _rate: 0,
        min: 0,
        max: 0,
        xMin: 0,
        xMax: 0,
        yMin: 0,
        yMax: 0
      },
      isDragging: false
    }
  },
  computed: {
    sliderStyle () {
      let per = (this.sliderData.rate * 100) + '%'
      let result = this.vertical ? {bottom: per} : {left: per}
      return result
    }
  },
  watch: {
    'sliderData.rate': {
      handler (n) {
        if (n !== this.sliderData._rate) this.$emit('change', n)
      },
      deep: true
    }
  },
  mounted () {
    window.addEventListener('resize', this.init)
    this.$nextTick(() => {
      this.init()
    })
  },
  methods: {
    init () {
      let max = this.sliderData.max || 0
      let info = null
      if (this.$refs.progress) {
        info = this.$refs.progress.getBoundingClientRect()
        max = this.vertical ? this.$refs.progress.clientHeight : this.$refs.progress.clientWidth
      } else if (document.querySelector(`#xVideoProgress_${this.id}`)) {
        let dom = document.querySelector(`#xVideoProgress_${this.id}`)
        info = dom.getBoundingClientRect()
        max = this.vertical ? dom.clientHeight : dom.clientWidth
      }
      this.$set(this.sliderData, 'v1', max * this.sliderData.rate)
      this.$set(this.sliderData, 'max', max)
      if (!info) return
      if (this.vertical) {
        this.$set(this.sliderData, 'yMin', info.y + info.height)
        this.$set(this.sliderData, 'yMax', info.y)
      } else {
        this.$set(this.sliderData, 'xMin', info.x)
        this.$set(this.sliderData, 'xMax', info.x + info.width)
      }
    },
    show () {
      if (this.sliderData.max === 0) this.init()
    },
    setValue (val) {
      if (this.isDragging) return
      let rate = val / this.sliderData.max
      this.$set(this.sliderData, 'v1', val)
      if (rate !== this.sliderData.rate) {
        this.$set(this.sliderData, 'rate', rate)
      }
    },
    setRate (rate) {
      if (this.isDragging || rate === this.sliderData.rate) return
      let val = this.sliderData.max * rate
      this.$set(this.sliderData, '_rate', rate)
      this.$set(this.sliderData, 'rate', rate)
      this.$set(this.sliderData, 'v1', val)
    },
    tapHandler: debounce(function (e) {
      let x = 0
      let y = 0
      if (e.type === 'mousedown') {
        x = e.clientX || 0
        y = e.clientY || 0
      } else if (e.type === 'touchstart') {
        x = e.touches[0].clientX || 0
        y = e.touches[0].clientY || 0
      }
      if (this.vertical) {
        let dv = Math.abs(y - this.sliderData.yMin)
        let total = Math.abs(this.sliderData.yMax - this.sliderData.yMin)
        let rate = total ? (dv / total) : this.sliderData.rate
        this.$set(this.sliderData, 'v1', y)
        this.$set(this.sliderData, 'rate', rate)
      } else {
        let dv = Math.abs(x - this.sliderData.xMin)
        let total = Math.abs(this.sliderData.xMax - this.sliderData.xMin)
        let rate = total ? (dv / total) : this.sliderData.rate
        this.$set(this.sliderData, 'v1', x)
        this.$set(this.sliderData, 'rate', rate)
      }
    }, 150),
    touchstartHandler (e) {
      this.isDragging = true
      let v0 = 0
      if (e.touches[0]) {
        v0 = this.vertical ? (e.touches[0].clientY || 0) : (e.touches[0].clientX || 0)
      }
      this.$set(this.sliderData, 'v0', v0)
      this.$set(this.sliderData, '_v0', this.sliderData.v1)
      window.addEventListener('touchmove', this.touchmoveHandler)
      window.addEventListener('touchend', this.touchendHandler)
    },
    touchmoveHandler (e) {
      if (this.isDragging && e.touches[0]) {
        let v1 = this.vertical ? (e.touches[0].clientY || 0) : (e.touches[0].clientX || 0)
        let dv = this.vertical ? (this.sliderData.v0 - v1) : (v1 - this.sliderData.v0)
        let result = Math.max(Math.min(this.sliderData._v0 + dv, this.sliderData.max), this.sliderData.min)
        let rate = this.sliderData.max ? (result / this.sliderData.max) : this.sliderData.rate
        this.$set(this.sliderData, 'v1', result)
        this.$set(this.sliderData, 'rate', rate)
      }
    },
    touchendHandler () {
      this.isDragging = false
      window.removeEventListener('touchmove', this.touchmoveHandler)
      window.removeEventListener('touchend', this.touchendHandler)
    },
    mousedownHandler (e) {
      this.isDragging = true
      let v0 = this.vertical ? (e.clientY || 0) : (e.clientX || 0)
      this.$set(this.sliderData, 'v0', v0)
      this.$set(this.sliderData, '_v0', this.sliderData.v1)
      window.addEventListener('mousemove', this.mousemoveHandler)
      window.addEventListener('mouseup', this.mouseupHandler)
      window.addEventListener('mouseleave', this.mouseupHandler)
    },
    mousemoveHandler (e) {
      if (this.isDragging) {
        let v1 = this.vertical ? (e.clientY || 0) : (e.clientX || 0)
        let dv = this.vertical ? (this.sliderData.v0 - v1) : (v1 - this.sliderData.v0)
        let result = Math.max(Math.min(this.sliderData._v0 + dv, this.sliderData.max), this.sliderData.min)
        let rate = this.sliderData.max ? (result / this.sliderData.max) : this.sliderData.rate
        this.$set(this.sliderData, 'v1', result)
        this.$set(this.sliderData, 'rate', rate)
      }
    },
    mouseupHandler () {
      this.isDragging = false
      window.removeEventListener('mousemove', this.mousemoveHandler)
      window.removeEventListener('mouseup', this.mouseupHandler)
      window.removeEventListener('mouseleave', this.mouseupHandler)
    },
    resetData () {
      this.$set(this.sliderData, 'rate', 0)
      this.$set(this.sliderData, 'v1', 0)
      this.isDragging = false
    }
  }
}
</script>
